package webapp.mvc.services.impl;

import com.google.common.base.Function;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.zoowii.jpa_utils.extension.Paginator;
import com.zoowii.jpa_utils.util.ListUtil;
import com.zoowii.jpa_utils.util.StringUtil;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import webapp.mvc.controllers.forms.RegisterForm;
import webapp.mvc.exceptions.UserException;
import webapp.mvc.models.*;
import webapp.mvc.services.IUserService;
import webapp.mvc.utils.EncryptUtil;
import webapp.mvc.utils.TimeUtil;

import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutionException;

@Service
public class UserService implements IUserService {
    private static final Logger LOG = LoggerFactory.getLogger(UserService.class);

    @Override
    public UserEntity checkUserAuth(String username, String password) throws UserException {
        UserEntity userEntity = UserEntity.find.findByUsername(username);
        if (userEntity == null) {
            throw new UserException("找不到用户" + username);
        }
        if (!EncryptUtil.checkPassword(userEntity.getPassword(), password)) {
            throw new UserException("密码错误");
        }
        return userEntity;
    }

    @Override
    public UserEntity registerUser(RegisterForm registerForm) throws UserException {
        UserEntity userEntity = UserEntity.find.findByUsername(registerForm.getUsername());
        if (userEntity != null) {
            throw new UserException("用户名" + registerForm.getUsername() + "已被注册");
        }
        userEntity = new UserEntity();
        userEntity.setUsername(registerForm.getUsername());
        userEntity.setPassword(EncryptUtil.encryptPassword(registerForm.getPassword()));
        if (registerForm.getNick() != null) {
            userEntity.setNick(registerForm.getNick());
        }
        boolean isFirstUser = UserEntity.find.where().limit(1).first() == null;
        userEntity.save();
        if (isFirstUser) {
            initRoles();
            addRoleToUser(userEntity.getId(), getAdminRole().getId());
        }
        if (registerForm.isShopOwner()) {
            initRoles();
            addRoleToUser(userEntity.getId(), getShopOwnerRole().getId());
        }
        return userEntity;
    }

    @Override
    public UserEntity changePassword(String userId, String password) throws UserException {
        UserEntity userEntity = UserEntity.find.byId(userId);
        if (userEntity == null) {
            throw new UserException("找不到用户" + userId);
        }
        if (StringUtil.isEmpty(password) || password.trim().length() < 4 || password.trim().length() > 30) {
            throw new UserException("密码需要4到30字符");
        }
        userEntity.setPassword(EncryptUtil.encryptPassword(password));
        userEntity.setModifyTime(TimeUtil.currentTimestamp());
        userEntity.update();
        return userEntity;
    }

    @Override
    public UserEntity findByUsername(String username) {
        return UserEntity.find.findByUsername(username);
    }

    @Override
    public UserEntity findById(String id) {
        return UserEntity.find.byId(id);
    }

    @Override
    public UserEntity findUserByWechatOpenId(String openId) {
        return UserEntity.find.where().eq("wechatUid", openId).first();
    }

    @Override
    public UserEntity findOrCreateUserByWechatOpenId(WxMpService wxMpService, String openId) throws UserException {
        UserEntity userEntity = findUserByWechatOpenId(openId);
        if (userEntity == null) {
            try {
                String lang = "zh_CN";
                WxMpUser wxMpUser = wxMpService.userInfo(openId, lang);
                LOG.info(wxMpUser.toString());
                userEntity = createWechatUserBinding(openId, wxMpUser.getNickname(), wxMpUser.getSexId(), null);
                return userEntity;
            } catch (Exception e) {
                throw new UserException(e);
            }
        } else {
            return userEntity;
        }
    }

    @Override
    public UserEntity createWechatUserBinding(String openId, String nickName, int gender, String phone) throws UserException {
        if (StringUtil.isEmpty(openId) || StringUtil.isEmpty(nickName)) {
            throw new UserException("openid和昵称不可为空");
        }
        if (findUserByWechatOpenId(openId) != null) {
            throw new UserException("此微信账号对应的用户已经存在");
        }
        UserEntity userEntity = new UserEntity();
        userEntity.setUsername(openId);
        userEntity.setNick(nickName);
        userEntity.setGender(gender);
        userEntity.setPhone(phone != null ? phone : "");
        userEntity.setWechatUid(openId);
        userEntity.setPassword(EncryptUtil.encryptPassword(UUID.randomUUID().toString()));
        userEntity.save();
        return userEntity;
    }

    @Override
    public void initRoles() {
        if (RoleEntity.find.where().limit(1).first() == null) {
            String[] roleNames = new String[]{"shop_owner", "admin", "shop_checker", "product_checker", "push_admin"};
            String[] roleDisplayNames = new String[]{"店主", "管理员", "商户审核员", "商品审核员", "推送管理员"};
            for (int i = 0; i < roleNames.length; ++i) {
                String roleName = roleNames[i];
                String roleDisplayName = roleDisplayNames[i];
                RoleEntity roleEntity = new RoleEntity();
                roleEntity.setDisplayName(roleDisplayName);
                roleEntity.setName(roleName);
                roleEntity.save();
            }
        }
    }

    @Override
    public List<RoleEntity> getAllRoles() {
        return RoleEntity.find.where().all();
    }

    @Override
    public RoleEntity getAdminRole() {
        try {
            return roleCache.get("admin");
        } catch (ExecutionException e) {
            LOG.error("cache error", e);
            return null;
        }
    }

    @Override
    public RoleEntity getShopOwnerRole() {
        try {
            return roleCache.get("shop_owner");
        } catch (ExecutionException e) {
            LOG.error("cache error", e);
            return null;
        }
    }

    @Override
    public RoleEntity getPushAdminRole() {
        try {
            return roleCache.get("push_admin");
        } catch (ExecutionException e) {
            LOG.error("cache error", e);
            return null;
        }
    }

    @Override
    public RoleEntity getShopCheckerRole() {
        try {
            return roleCache.get("shop_checker");
        } catch (ExecutionException e) {
            LOG.error("cache error", e);
            return null;
        }
    }

    @Override
    public RoleEntity getProductCheckerRole() {
        try {
            return roleCache.get("product_checker");
        } catch (ExecutionException e) {
            LOG.error("cache error", e);
            return null;
        }
    }

    @Override
    public void addRoleToUser(String userId, String roleId) {
        if (StringUtil.isEmpty(userId) || StringUtil.isEmpty(roleId)) {
            return;
        }
        UserRoleEntity userRoleEntity = UserRoleEntity.find.where().eq("userId", userId).eq("roleId", roleId).first();
        if (userRoleEntity == null) {
            userRoleEntity = new UserRoleEntity();
            userRoleEntity.setUserId(userId);
            userRoleEntity.setRoleId(roleId);
            userRoleEntity.save();
        }
    }

    @Override
    public void removeRoleFromUser(String userId, String roleId) {
        if (StringUtil.isEmpty(userId) || StringUtil.isEmpty(roleId)) {
            return;
        }
        UserRoleEntity userRoleEntity = UserRoleEntity.find.where().eq("userId", userId).eq("roleId", roleId).first();
        if (userRoleEntity != null) {
            userRoleEntity.delete();
        }
    }

    @Override
    public List<RoleEntity> getRolesOfUser(String userId) {
        List<UserRoleEntity> userRoleEntities = UserRoleEntity.find.where().eq("userId", userId).all();
        return ListUtil.map(userRoleEntities, new Function<UserRoleEntity, RoleEntity>() {
            @Override
            public RoleEntity apply(UserRoleEntity userRoleEntity) {
                return RoleEntity.find.byId(userRoleEntity.getRoleId());
            }
        });
    }

    @Override
    public List<UserEntity> getUsers(Paginator paginator) {
        List<UserEntity> userEntities = UserEntity.find.findByPaginator(paginator);
        ListUtil.map(userEntities, new Function<UserEntity, Void>() {
            @Override
            public Void apply(UserEntity userEntity) {
                userEntity.setRoles(getRolesOfUser(userEntity.getId()));
                return null;
            }
        });
        return userEntities;
    }

    @Override
    public WechatPushContentEntity saveWechatPushContent(String contentId, String title, String jsonStr) {
        WechatPushContentEntity wechatPushContentEntity = WechatPushContentEntity.find.byId(contentId);
        boolean isNew = wechatPushContentEntity == null;
        if (isNew) {
            wechatPushContentEntity = new WechatPushContentEntity();
        }
        wechatPushContentEntity.setContent(jsonStr);
        wechatPushContentEntity.setTitle(title != null ? title : "");
        if (isNew) {
            wechatPushContentEntity.save();
        } else {
            wechatPushContentEntity.update();
        }
        return wechatPushContentEntity;
    }

    @Override
    public WechatPushHistoryEntity saveWechatPushHistory(String contentId, String userId) {
        WechatPushHistoryEntity wechatPushHistoryEntity = new WechatPushHistoryEntity();
        wechatPushHistoryEntity.setContentId(contentId);
        wechatPushHistoryEntity.setUserId(userId);
        wechatPushHistoryEntity.save();
        return wechatPushHistoryEntity;
    }

    @Override
    public List<WechatPushContentEntity> getWechatPushContents(Paginator paginator) {
        paginator.getOrders().add(Pair.of("createTime", false));
        return WechatPushContentEntity.find.findByPaginator(paginator);
    }

    @Override
    public WechatPushContentEntity findWechatPushContentById(String id) {
        return WechatPushContentEntity.find.byId(id);
    }

    @Override
    public List<WechatPushHistoryEntity> getWechatPushHistories(String contentId) {
        return WechatPushHistoryEntity.find.where().eq("contentId", contentId).orderBy("createTime", false).all();
    }

    @Override
    public void deleteWechatPushContent(String contentId) {
        WechatPushContentEntity wechatPushContentEntity = WechatPushContentEntity.find.byId(contentId);
        if (wechatPushContentEntity != null) {
            wechatPushContentEntity.delete();
        }
    }

    private LoadingCache<String, RoleEntity> roleCache = CacheBuilder.newBuilder()
            .maximumSize(100)
            .build(new CacheLoader<String, RoleEntity>() {
                @Override
                public RoleEntity load(String name) throws Exception {
                    initRoles();
                    return RoleEntity.find.where().eq("name", name).first();
                }
            });
}
